package com.lst.tools.httpclient.service.impl;

import com.lst.tools.basics.function.OneParamFunction;
import com.lst.tools.httpclient.service.IHttpClientService;
import com.lst.tools.httpclient.vo.HttpClientVo;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 跨域业务层接口实现
 *
 * @author: Frank
 * @date: 2019/09/24 13:51
 * @version: V1.0
 */
@Service
public class HttpClientService implements IHttpClientService {

    private static final String DEFAULT_CHARSET = "utf-8";
    private static final String KEY_STORE_TYPE = "PKCS12";
    private static final String SUPPORTED_PROTOCOLS_TLS_V1 = "TLSv1";

    @Autowired(required = false)
    private CloseableHttpClient httpClient;

    @Autowired(required = false)
    private RequestConfig requestConfig;

    @Override
    public HttpClientVo doGet(String url) throws IOException, URISyntaxException {
        return this.doGet(url, null);
    }

    @Override
    public HttpClientVo doGet(String url, Map<String, Object> params) throws IOException, URISyntaxException {
        return this.doGet(url, params, null);
    }

    @Override
    public HttpClientVo doGet(String url, Map<String, Object> params, OneParamFunction<Void, HttpGet> fun) throws IOException, URISyntaxException {
        // 创建请求地址uri
        URIBuilder uriBuilder = new URIBuilder(url);
        // 设置参数
        if (MapUtils.isNotEmpty(params)) {
            Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
            }
        }
        // 创建httpGet连接
        HttpGet httpGet = new HttpGet(uriBuilder.toString());
        httpGet.setConfig(requestConfig);
        if (fun != null) {
            fun.callback(httpGet);
        }
        return this.getHttpResult(httpClient, httpGet);
    }

    @Override
    public InputStream doGetReturnStream(String url) throws IOException {
        // 创建httpPost连接
        HttpGet httpGet = new HttpGet(url);

        httpGet.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        // 设置配置信息
        httpGet.setConfig(requestConfig);

        // 利用httpClient执行httpGet请求
        HttpResponse response = httpClient.execute(httpGet);
        if (response == null) {
            return null;
        }
        HttpEntity resEntity = response.getEntity();
        return resEntity == null ? null : resEntity.getContent();
    }

    @Override
    public HttpClientVo doPost(String url) throws IOException {
        return doPost(url, null);
    }

    @Override
    public HttpClientVo doPost(String url, Map<String, Object> params) throws IOException {
        return this.doPost(url, params, null);
    }

    @Override
    public HttpClientVo doPost(String url, Map<String, Object> params, OneParamFunction<Void, HttpPost> fun) throws IOException {
        // 创建httpPost连接
        HttpPost httpPost = new HttpPost(url);

        // 处理请求参数
        if (params != null) {
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();

            Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, DEFAULT_CHARSET));
        }

        // 设置配置信息
        httpPost.setConfig(requestConfig);
        if (fun != null) {
            fun.callback(httpPost);
        }
        return this.getHttpResult(httpClient, httpPost);
    }

    @Override
    public HttpClientVo doPostJson(String url, String params) throws IOException {
        return this.doPostJson(url, params, null);
    }

    @Override
    public HttpClientVo doPostJson(String url, String params, OneParamFunction<Void, HttpPost> fun) throws IOException {
        // 创建httpPost连接
        HttpPost httpPost = new HttpPost(url);

        // 处理请求参数
        if (params != null) {
            httpPost.setEntity(new StringEntity(params, ContentType.APPLICATION_JSON));
        }
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        // 设置配置信息
        httpPost.setConfig(requestConfig);
        if (fun != null) {
            fun.callback(httpPost);
        }
        return this.getHttpResult(httpClient, httpPost);
    }

    @Override
    public InputStream doPostJsonReturnStream(String url, String params) throws IOException {
        // 创建httpPost连接
        HttpPost httpPost = new HttpPost(url);

        // 处理请求参数
        if (params != null) {
            httpPost.setEntity(new StringEntity(params, ContentType.APPLICATION_JSON));
        }
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        // 设置配置信息
        httpPost.setConfig(requestConfig);

        // 利用httpClient执行httpGet请求
        HttpResponse response = httpClient.execute(httpPost);
        if (response == null) {
            return null;
        }
        HttpEntity resEntity = response.getEntity();
        return resEntity == null ? null : resEntity.getContent();
    }

    @Override
    public HttpClientVo doPostXml(String url, String xml) throws IOException {
        return this.doPostXml(url, xml, DEFAULT_CHARSET);
    }

    @Override
    public HttpClientVo doPostXml(String url, String params, String charset) throws IOException {
        return this.doPostXml(url, params, charset, null);
    }

    @Override
    public HttpClientVo doPostXml(String url, String params, String charset, OneParamFunction<Void, HttpPost> fun) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        // 处理请求参数
        if (StringUtils.isNotBlank(params)) {
            StringEntity entity = new StringEntity(params, charset);
            entity.setContentType(ContentType.TEXT_XML.toString());
            httpPost.setEntity(entity);
        }
        // 设置配置信息
        httpPost.setConfig(requestConfig);
        if (fun != null) {
            fun.callback(httpPost);
        }
        return getHttpResult(httpClient, httpPost, charset);
    }

    @Override
    public HttpClientVo doPostXmlByCertAndKeyPassword(String url, String xml, String certFilePath, char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        return this.doPostXmlByCertAndKeyPassword(url, xml, certFilePath, keyPassword, null);
    }

    @Override
    public HttpClientVo doPostXmlByCertAndKeyPassword(String url, String xml, String certFilePath, char[] keyPassword, OneParamFunction<Void, HttpPost> fun) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
        FileInputStream fileInputStream = new FileInputStream(new File(certFilePath));
        try {
            keyStore.load(fileInputStream, keyPassword);
        } finally {
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, keyPassword).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{SUPPORTED_PROTOCOLS_TLS_V1}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        // 创建httpPost连接
        HttpPost httpPost = new HttpPost(url);
        // 处理请求参数
        if (StringUtils.isNotBlank(xml)) {
            StringEntity entity = new StringEntity(xml, DEFAULT_CHARSET);
            entity.setContentType(ContentType.TEXT_XML.toString());
            httpPost.setEntity(entity);
        }
        if (fun != null) {
            fun.callback(httpPost);
        }
        return this.getHttpResult(httpClient, httpPost);
    }

    private HttpClientVo getHttpResult(CloseableHttpClient httpClient, HttpUriRequest httpUriRequest) throws IOException {
        return this.getHttpResult(httpClient, httpUriRequest, DEFAULT_CHARSET);
    }

    private HttpClientVo getHttpResult(CloseableHttpClient httpClient, HttpUriRequest httpUriRequest, String charset) throws IOException {
        CloseableHttpResponse response = null;
        try {
            // 利用httpClient执行httpGet请求
            response = httpClient.execute(httpUriRequest);
            // 处理结果
            String content = EntityUtils.toString(response.getEntity(), charset);
            return new HttpClientVo(response.getStatusLine().getStatusCode(), content);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }
}
